#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QSplitter>
#include <QDebug>

#include <QMessageBox>
#include <QItemSelectionModel>
#include <QModelIndexList>
#include <QList>
#include <QDataStream>
#include <QtEndian>
#include "core/core.h"
#include "settings/isettings.h"
#include "configdialog.h"
#include "uimodels.h"
#include "mystatusbar.h"

#include "../core/beaverthread.h"


class MainWindow::MainWindowPrivate{
public:
	MainWindowPrivate();
	~MainWindowPrivate();

	BeaverThread *m_pThread;
	IDatabase *m_pDB;
	KeyPressFilter *m_pKPF;
	MyStatusBar *m_pStatusBar;
};

MainWindow::MainWindowPrivate::MainWindowPrivate(){
	m_pThread = NULL;
	m_pDB = NULL;
	m_pKPF = NULL;
	m_pStatusBar = NULL;
}

MainWindow::MainWindowPrivate::~MainWindowPrivate(){

	m_pThread->wait();
	delete m_pThread;

}

//end od d-class
//------------------------------------------------------------------------------
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
	ui(new Ui::MainWindow),
	d(new MainWindowPrivate())
{
    ui->setupUi(this);

	setWindowTitle( tr("Beaver-feeds") );

	d->m_pStatusBar = new MyStatusBar( this );
	installEventFilter( d->m_pStatusBar );

	ui->centralWidget->layout()->addWidget( d->m_pStatusBar );


	connect( ui->actionAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt()) );
	connect( ui->actionExit, SIGNAL(triggered()), this, SLOT(close()) );
	connect( ui->actionConfig, SIGNAL(triggered() ),
			 this, SLOT(showConfigDialog()) );
	connect( ui->actionConnect, SIGNAL(triggered()), this, SLOT(tryConnect()) );

	QSplitter *mainSplitter = new QSplitter(ui->centralWidget);

	ui->centralWidget->layout()->itemAt(0)->widget()->
											layout()->addWidget(mainSplitter);

	QSplitter *secondSplitter = new QSplitter(Qt::Vertical, mainSplitter);

	mainSplitter->addWidget(ui->listView);
	mainSplitter->addWidget(secondSplitter);
	secondSplitter->addWidget(ui->tableView);
	secondSplitter->addWidget(ui->webView);


#ifdef Q_WS_WIN
	d->m_pDB = qobject_cast<IDatabase*>
								(Core::instance()->database("sqlite.dll"));
#elif defined Q_WS_MAC
	setUnifiedTitleAndToolBarOnMac( true );
	d->m_pDB = qobject_cast<IDatabase*>
							(Core::instance()->database("libsqlite.dylib"));
#else
	d->m_pDB = qobject_cast<IDatabase*>
							(Core::instance()->database("libsqlite.so"));
#endif
	if( d->m_pDB && d->m_pDB->canConnect() ){
		d->m_pDB->init( Core::instance() );
		d->m_pDB->connectToDB();
	}else{
		qFatal("coś nie tak z bazą");
	}

	UIModels *models = new UIModels( this );
	ui->listView->setModel( models->getListModel() );
	ui->listView->setItemDelegate( models->getListDelgate() );

	ui->listView->setSizePolicy( QSizePolicy::MinimumExpanding,
								 QSizePolicy::Expanding);

	ui->listView->setMinimumWidth( 150 );
	ui->listView->setMaximumWidth( 350 );


	d->m_pThread = new BeaverThread();
	connect( d->m_pThread, SIGNAL(started()), this, SLOT(threadStarted()) );
	connect( d->m_pThread, SIGNAL(connected()), this, SLOT(connectSlot()) );
	connect( d->m_pThread, SIGNAL(encrypted()), this, SLOT(connectSlotE()) );
	connect( d->m_pThread, SIGNAL(disconnected()),
			 this, SLOT(disconnectSlot()) );
	connect( d->m_pThread, SIGNAL(hostNotFound()),
			 this, SLOT(hostNotFoundSlot()) );
	connect( d->m_pThread, SIGNAL(connectionRefused()),
			 this, SLOT(connectionRefusedSlot()) );
	connect( d->m_pThread, SIGNAL(sslError(QStringList)),
			 this, SLOT(sslErrorSlot(QStringList)) );


	d->m_pKPF = new KeyPressFilter(this);



	ui->tableView->setItemDelegate( models->getTableModel() );
	ui->tableView->setAlternatingRowColors( true );
	ui->tableView->setModel( d->m_pDB->getModel(1) );



	const QSortFilterProxyModel* tmpModel =
			dynamic_cast<const QSortFilterProxyModel*>(ui->tableView->model());
	connect( this, SIGNAL(itemReaded(QModelIndex)),
			 tmpModel->sourceModel(), SLOT(itemReaded(const QModelIndex &) ));
	connect( this, SIGNAL(itemUnreaded(QModelIndex)),
			 tmpModel->sourceModel(), SLOT(itemUnreaded(const QModelIndex &) ));
	connect( this, SIGNAL(itemDeleted(QList<int>)),
			 tmpModel->sourceModel(), SLOT(itemDeleted(QList<int>) ));

	ui->tableView->setSelectionBehavior( QAbstractItemView::SelectRows );
	ui->tableView->setSelectionMode( QAbstractItemView::ExtendedSelection );
	ui->tableView->horizontalHeader()->setMovable( true );

	ui->tableView->verticalHeader()->hide();
	ui->tableView->horizontalHeader()->setHighlightSections( false );
	ui->tableView->horizontalHeader()->setSortIndicatorShown( true );
	ui->tableView->horizontalHeader()->setResizeMode( 0, QHeaderView::Stretch );
	ui->tableView->horizontalHeader()->setResizeMode( 1, QHeaderView::Fixed );
	ui->tableView->horizontalHeader()->setResizeMode( 2,
												QHeaderView::ResizeToContents );
	ui->tableView->resizeColumnsToContents();
	ui->tableView->setSortingEnabled( true );
	ui->tableView->installEventFilter( d->m_pKPF );

	connect( ui->tableView, SIGNAL(clicked(QModelIndex)),
			 this, SLOT(showItem(QModelIndex)) );

	connect( d->m_pKPF, SIGNAL(enterPressed(QModelIndex)),
			 this, SLOT(showItem(QModelIndex)) );

	//ui->webView->load(QUrl("http://test.momus.com.pl/"));
	//ui->webView->show();


	connect( ui->actionMarkAsReaded, SIGNAL(triggered()),
			 this, SLOT(markAs()) );
	connect( ui->actionMarkAsUnreaded, SIGNAL(triggered()),
			 this, SLOT(markAs()) );
	connect( ui->actionMarkAsDeleted, SIGNAL(triggered()),
			 this, SLOT(markAs()) );
	connect( d->m_pKPF, SIGNAL(deletePressed()),
			 ui->actionMarkAsDeleted, SIGNAL(triggered()) );

	QByteArray state = Core::instance()->settings()->value(
			Setting::mainWindowStateKey, QVariant() ).toByteArray();

	restoreState( state );

	QByteArray geo = Core::instance()->settings()->value(
			Setting::mainWindowGeometryKey, QVariant() ).toByteArray();

	restoreGeometry( geo );


}

void MainWindow::threadStarted(){
	bool autoConnect = Core::instance()->settings()->value(
			Setting::autoConnectKey, Setting::autoConnectDef ).toBool();

	if( autoConnect ){
		tryConnect();
	}
}


MainWindow::~MainWindow(){
	QMetaObject::invokeMethod(d->m_pThread, "stop");

	delete ui;
	delete d;

	const QList<ISettings*>& list = Core::instance()->getSettingsPages();

	while( list.count() ){
		ISettings *tmp = list.at(0);
		Core::instance()->unregisterSettingPage( tmp );
		delete tmp;
	}

	Core::drop();
}

void MainWindow::closeEvent(QCloseEvent *){
	QByteArray state = saveState();
	Core::instance()->settings()->setValue( Setting::mainWindowStateKey,
											state );
	QByteArray geo = saveGeometry();
	Core::instance()->settings()->setValue( Setting::mainWindowGeometryKey,
											geo );

	qApp->quit();
}

void MainWindow::showItem(QModelIndex proxyItem){
	QModelIndex source = dynamic_cast<const QSortFilterProxyModel*>
			(proxyItem.model())->mapToSource( proxyItem );

	emit itemReaded(source);

	int row = proxyItem.data(Qt::UserRole + 1).toInt();

	ui->tableView->update( ui->tableView->model()->index(proxyItem.row(), 1) );

	ui->webView->setHtml( d->m_pDB->getText( row ) );
}


void MainWindow::markAs(){
	QModelIndexList list = ui->tableView->selectionModel()->selectedRows();

	QModelIndexList::const_iterator it;

	QModelIndex source;
	QList<int> toDeleted;
	for( it = list.constBegin(); it != list.constEnd(); it++ ){
		source = dynamic_cast<const QSortFilterProxyModel*>
				((*it).model())->mapToSource( (*it) );

		if( sender() == ui->actionMarkAsReaded )
			emit itemReaded( source );
		else if( sender() == ui->actionMarkAsUnreaded )
			emit itemUnreaded(source);
		else if( sender() == ui->actionMarkAsDeleted ){
			toDeleted.push_back( source.row() );
		}

		ui->tableView->update(
				ui->tableView->model()->index( (*it).row(), 1) );
	}

	if( sender() == ui->actionMarkAsDeleted)
		emit itemDeleted( toDeleted );

}

void MainWindow::showConfigDialog(){
	ConfigDialog *dialog = new ConfigDialog();

	connect(dialog, SIGNAL(closed()), dialog, SLOT(deleteLater()));
	connect(qApp, SIGNAL(aboutToQuit()), dialog, SLOT(deleteLater()));

	dialog->show();
}

void MainWindow::tryConnect(){
	if( !d->m_pThread->isConnected() ){
		QString host = Core::instance()->settings()->value(
				Setting::serverAddrKey, Setting::serverAddrDef )
					   .toString() ;
		quint32 port = Core::instance()->settings()->value(
				Setting::serverPortKey, Setting::serverPortDef ).toInt();


		QMetaObject::invokeMethod(d->m_pThread, "connectToHostEncrypted",
								  Qt::QueuedConnection,
								  Q_ARG(QString, host),
								  Q_ARG(quint16, port));
		d->m_pStatusBar->setConnectionStatusText( tr("Trwa łączenie") );
		d->m_pStatusBar->setConnectionStatus( MyStatusBar::Connecting );
	}else{
		QMetaObject::invokeMethod(d->m_pThread, "disconnectFromHost" );
	}
}

void MainWindow::connectSlot(){
	d->m_pStatusBar->setConnectionStatus( MyStatusBar::Connected );
	d->m_pStatusBar->setConnectionStatusText( tr("Połączono z serwerem") );

	ui->actionConnect->setIcon( QIcon(":/res/edit-delete.png") );
	ui->actionConnect->setText( tr("Rozłącz") );
}

void MainWindow::connectSlotE(){
	d->m_pStatusBar->setConnectionStatus( MyStatusBar::Encrypted );
	d->m_pStatusBar->setConnectionStatusText(
			tr("Połączono z serwerem (zaszyfrowano)") );

	//TODO
	//change text??
	ui->actionConnect->setIcon( QIcon(":/res/edit-delete.png") );
	ui->actionConnect->setText( tr("Rozłącz") );

	QByteArray block;
	QDataStream out(&block, QIODevice::WriteOnly);
	out.setVersion(QDataStream::Qt_4_7);
	out << (quint32)0;
	out.writeRawData( "USERLOGIN\n", strlen("USERLOGIN\n") );

	QByteArray login = Core::instance()->settings()->value(
			Setting::userNameKey, Setting::userNameDef
			).toByteArray();
	QByteArray password = Core::instance()->settings()->value(
			Setting::passwordKey, Setting::passwordDef
			).toByteArray();

	out.writeRawData( login.data(), login.size() );
	out.writeRawData( "\n", 1 );
	out.writeRawData( password.data(), password.size() );

	out.device()->seek(0);
	out << (quint32)(block.size() - sizeof(quint32));

	QMetaObject::invokeMethod(d->m_pThread, "send",
							  Q_ARG(QByteArray, block));
}

void MainWindow::disconnectSlot(){
	d->m_pStatusBar->setConnectionStatus( MyStatusBar::Disconnected );
	d->m_pStatusBar->setConnectionStatusText( tr("Nie połączono") );

	ui->actionConnect->setIcon( QIcon(":/res/quickopen.png") );
	ui->actionConnect->setText( tr("Połącz") );

	qApp->beep();
}

void MainWindow::hostNotFoundSlot(){
	d->m_pStatusBar->setConnectionStatus( MyStatusBar::Disconnected );
	d->m_pStatusBar->setConnectionStatusText( tr("Nie połączono") );

	bool errorConnect = Core::instance()->settings()->value(
			Setting::errorsConnectKey, Setting::errorsConnectDef ).toBool();

	if( errorConnect ){
		QMessageBox::warning( this, tr("Ostrzeżenie"),
							  tr("Nie znaleziono serwera,\n"
								 "sprawdź adres") );
	}else{
		qApp->beep();
		d->m_pStatusBar->showMessage( tr("Nie znaleziono serwera, "
										 "sprawdź adres"), 5000 );
	}
}

void MainWindow::connectionRefusedSlot(){
	d->m_pStatusBar->setConnectionStatus( MyStatusBar::Disconnected );
	d->m_pStatusBar->setConnectionStatusText( tr("Nie połączono") );

	bool errorConnect = Core::instance()->settings()->value(
			Setting::errorsConnectKey, Setting::errorsConnectDef ).toBool();

	if( errorConnect ){
		QMessageBox::warning( this, tr("Ostrzeżenie"),
							  tr("Nie można połączyć się z serwerem,\n"
								 "sprawdź port") );
	}else{
		qApp->beep();
		d->m_pStatusBar->showMessage( tr("Nie można połączyć się z serwerem, "
										 "sprawdź port"), 5000 );
	}


}

void MainWindow::sslErrorSlot(QStringList error){
	d->m_pStatusBar->setConnectionStatus( MyStatusBar::Disconnected );
	d->m_pStatusBar->setConnectionStatusText( tr("Nie połączono") );
}
